Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: [v0.8-develop] Add allowlist sample hook, refactor test base #70

Merged
merged 1 commit into from
Jul 10, 2024

Conversation

adamegyed
Copy link
Contributor

Motivation

To provide better context for how pre-validation hooks should be used, it would help to provide some real use cases. A simple case is an allowlist plugin - limiting the addresses, and possibly the selectors, of the targets of execute and executeBatch when using a specific validation function.

Solution

  • Implement a simple allowlist hook plugin.
  • Update the (temporary) custom validation initialization workflow for more flexibility.
  • Provide a fuzz test suite to ensure it works correctly.
  • To simplify existing and new tests:
    • Pull some common workflows for constructing user ops and runtime calls out to AccountTestBase.
    • Add a new extension to AccountTestBase called CustomValidationTestBase, and update the Per-hook data test (and the allowlist test) to use this.

@adamegyed adamegyed requested a review from a team June 11, 2024 18:42
override
returns (uint256)
{
if (functionId == uint8(FunctionId.PRE_VALIDATION_HOOK)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the point of this check, and the similar one in the runtime validation hook?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In other plugin examples we've been using an if-else chain for the internal function dispatcher, so I was following that pattern here too. I suppose we could cut it and just treat every function id the same here, because this one doesn't have multiple implementations.

Copy link
Collaborator

@fangting-alchemy fangting-alchemy Jun 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like the idea of not having the functionId, especially the plugins are global singleton.

Actually, we should be explicit since we do ask the functionId var to be passed.

}

// solhint-disable-next-line no-empty-blocks
function pluginManifest() external pure override returns (PluginManifest memory) {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this blank because it's supposed to only be installed with a user-provided config?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, basically. The manifest is empty because this plugin doesn't define anything that the manifest can install:

  • execution functions
  • execution hooks
  • validation functions
  • signature validators
  • call permissions

@adamegyed adamegyed force-pushed the adam/per-hook-data branch from 62344f2 to 0e67b55 Compare June 12, 2024 18:48
@adamegyed adamegyed force-pushed the adam/sample-allowlist-hook branch from b8637b4 to 4c147bf Compare June 12, 2024 18:51
@adamegyed adamegyed force-pushed the adam/per-hook-data branch from 0e67b55 to c2da4d0 Compare June 14, 2024 15:57
@adamegyed adamegyed force-pushed the adam/sample-allowlist-hook branch 2 times, most recently from cd1637f to 8c7133c Compare June 14, 2024 16:06
);

vm.deal(address(account1), 100 ether);
_customValidationSetup();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do the old tests here test allowList fails?
Are the error thrown from AllowlistPlugin captured as AA errors?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, depending on the call path, they throw either user op or runtime errors, and the fuzzer expects them to be called.

The error to look for is loaded from here:

function _getExpectedUserOpError(Call[] memory calls) internal view returns (bytes memory) {
for (uint256 i = 0; i < calls.length; i++) {
Call memory call = calls[i];
(bool allowed, bool hasSelectorAllowlist) =
allowlistPlugin.targetAllowlist(call.target, address(account1));
if (allowed) {
if (
hasSelectorAllowlist
&& !allowlistPlugin.selectorAllowlist(call.target, bytes4(call.data), address(account1))
) {
return abi.encodeWithSelector(
IEntryPoint.FailedOpWithRevert.selector,
0,
"AA23 reverted",
abi.encodeWithSelector(AllowlistPlugin.SelectorNotAllowed.selector)
);
}
} else {
return abi.encodeWithSelector(
IEntryPoint.FailedOpWithRevert.selector,
0,
"AA23 reverted",
abi.encodeWithSelector(AllowlistPlugin.TargetNotAllowed.selector)
);
}
}
return "";
}
function _getExpectedRuntimeError(Call[] memory calls) internal view returns (bytes memory) {
for (uint256 i = 0; i < calls.length; i++) {
Call memory call = calls[i];
(bool allowed, bool hasSelectorAllowlist) =
allowlistPlugin.targetAllowlist(call.target, address(account1));
if (allowed) {
if (
hasSelectorAllowlist
&& !allowlistPlugin.selectorAllowlist(call.target, bytes4(call.data), address(account1))
) {
return abi.encodeWithSelector(
UpgradeableModularAccount.PreRuntimeValidationHookFailed.selector,
address(allowlistPlugin),
uint8(AllowlistPlugin.FunctionId.PRE_VALIDATION_HOOK),
abi.encodeWithSelector(AllowlistPlugin.SelectorNotAllowed.selector)
);
}
} else {
return abi.encodeWithSelector(
UpgradeableModularAccount.PreRuntimeValidationHookFailed.selector,
address(allowlistPlugin),
uint8(AllowlistPlugin.FunctionId.PRE_VALIDATION_HOOK),
abi.encodeWithSelector(AllowlistPlugin.TargetNotAllowed.selector)
);
}
}

And the test expects them here:

vm.expectRevert(expectedRevertData);
vm.expectRevert(expectedRevertData);

@adamegyed adamegyed force-pushed the adam/per-hook-data branch from 64a9917 to 23cdc39 Compare June 19, 2024 15:06
@adamegyed adamegyed force-pushed the adam/sample-allowlist-hook branch from 8c7133c to d48a9a4 Compare June 19, 2024 15:07
Copy link
Collaborator

@fangting-alchemy fangting-alchemy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@adamegyed adamegyed force-pushed the adam/per-hook-data branch from 23cdc39 to 4599a2d Compare June 20, 2024 17:53
@adamegyed adamegyed force-pushed the adam/sample-allowlist-hook branch from d48a9a4 to 9bc6789 Compare June 20, 2024 17:53
@adamegyed adamegyed force-pushed the adam/per-hook-data branch from 4599a2d to 8a09d31 Compare June 20, 2024 20:37
@adamegyed adamegyed force-pushed the adam/sample-allowlist-hook branch from 9bc6789 to 17906b1 Compare June 20, 2024 20:37
@adamegyed adamegyed force-pushed the adam/per-hook-data branch from 8a09d31 to d402361 Compare June 20, 2024 20:53
@adamegyed adamegyed force-pushed the adam/sample-allowlist-hook branch from 17906b1 to e3ab9a2 Compare June 20, 2024 20:53
@adamegyed adamegyed force-pushed the adam/per-hook-data branch from d402361 to 60916fa Compare June 20, 2024 21:01
@adamegyed adamegyed force-pushed the adam/sample-allowlist-hook branch from e3ab9a2 to ae5c79a Compare June 20, 2024 21:05
function pluginManifest() external pure override returns (PluginManifest memory) {}

function _checkAllowlistCalldata(bytes calldata callData) internal view {
if (bytes4(callData[:4]) == IStandardExecutor.execute.selector) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Callout: when executeUserOp is used, we need to check calldata[4:8] instead

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this hook doesn't depend on the contents of executeUserOp, then the account shouldn't send the user op, right? I guess this depends on how we do it, but it seems like the account should know whether to use msg.data or userOp.callData depending on the hook config.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah agree that it should be abstracted from plugins. in the current permissions stack, we directly send over userOp.callData which could be abi.encodeCall(...) or abi.encodePacked(executeUserOp.selector, abi.encodeCall(...)) for hooks that don't require UO context, and for hooks that require UO context, they would always receive abi.encodePacked(executeUserOp.selector, abi.encodeCall(...)). Dislike that part and would likely change that though

Copy link
Contributor

@huaweigu huaweigu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Additionally, we developed a 6900 address book plugin that utilized the pre-validation hook.

@adamegyed adamegyed force-pushed the adam/sample-allowlist-hook branch from ae5c79a to 5785dba Compare June 26, 2024 16:20
@adamegyed adamegyed force-pushed the adam/per-hook-data branch from 2afe76b to 94b7592 Compare June 26, 2024 16:22
@adamegyed adamegyed force-pushed the adam/sample-allowlist-hook branch from 5785dba to 2a965ac Compare June 26, 2024 16:25
@adamegyed adamegyed force-pushed the adam/per-hook-data branch from 94b7592 to e118421 Compare June 27, 2024 19:27
@adamegyed adamegyed force-pushed the adam/sample-allowlist-hook branch from 2a965ac to 4e0fdc3 Compare June 27, 2024 19:29
@adamegyed adamegyed force-pushed the adam/per-hook-data branch from e118421 to 94a3764 Compare June 27, 2024 20:07
@adamegyed adamegyed force-pushed the adam/sample-allowlist-hook branch from 4e0fdc3 to 18abe0f Compare June 27, 2024 20:10
@adamegyed adamegyed force-pushed the adam/per-hook-data branch from 94a3764 to e4526cc Compare June 28, 2024 15:29
@adamegyed adamegyed force-pushed the adam/sample-allowlist-hook branch from 18abe0f to 17ed4e0 Compare June 28, 2024 15:30
@adamegyed adamegyed force-pushed the adam/per-hook-data branch from e4526cc to 68a5847 Compare June 28, 2024 18:38
@adamegyed adamegyed force-pushed the adam/sample-allowlist-hook branch from 17ed4e0 to 78ae1eb Compare June 28, 2024 18:38
@adamegyed adamegyed force-pushed the adam/per-hook-data branch from 68a5847 to b579102 Compare July 9, 2024 20:46
Base automatically changed from adam/per-hook-data to v0.8-develop July 9, 2024 20:51
@adamegyed adamegyed force-pushed the adam/sample-allowlist-hook branch from 78ae1eb to 5c10b0d Compare July 10, 2024 14:45
@adamegyed adamegyed merged commit d8c726c into v0.8-develop Jul 10, 2024
3 checks passed
@adamegyed adamegyed deleted the adam/sample-allowlist-hook branch July 10, 2024 14:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants